---
id: dbcontext-sql-proxy
title: 9.17 Sql 高级代理
sidebar_label: 9.17 Sql 高级代理
---

## 9.17.1 关于 `Sql` 代理

`Sql` 代理是 `Fur` 框架中对 `Sql` 操作一个非常重要的概念，通过这种方式可以大大提高 `Sql` 书写效率，而且后期极易维护。

`Sql` 代理属于 `Fur` 框架中一个高级功能。

## 9.17.2 了解 `ISqlDispatchProxy`

`ISqlDispatchProxy` 接口是 `Fur` 实现**被代理接口**的唯一依赖，任何公开的接口一旦集成了 `ISqlDispatchProxy` 接口，那么这个接口就是**被托管拦截**的 `Sql` 操作接口。

简单定义一个 **Sql 代理接口**

```cs {1,5}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
    }
}
```

一旦这个接口继承了 `ISqlDispatchProxy`，那么它就会**动态创建接口实例，而且支持依赖注入/控制反转获取实例**。

## 9.17.3 开始领略 `Sql` 代理

下面我讲通过多个例子来演示 `Sql` 代理的用法，为什么推荐这种方式操作 `Sql`。

支持各种方式获取实例：

### 9.17.3.1 构造函数方式

```cs {1-2}
private readonly Isql _sql;
public FurService(Isql sql)
{
    _sql = sql;
}
```

### 9.17.3.2 方法参数注入

```cs {1}
public async Task<List<PersonDto>> GetAll([FromServices] Isql, string keyword)
{
}
```

### 9.17.3.3 `App.GetSqlDispatchProxy<ISql>()`

```cs
var sql = App.GetSqlDispatchProxy<ISql>();
```

## 9.17.4 `Sql` 操作

### 9.17.4.1 返回 `DataTable`

```cs {8,12,16,20}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        // 执行sql并传入参数，基元类型
        [SqlExecute("select * from person where id >@id and name like %@name%")]
        DataTable GetPerson(int id, string name);

        // 执行sql并传入参数，对象类型
        [SqlExecute("select * from person where id >@id and name like %@name%")]
        DataTable GetPerson(MyParam paras));

        // 执行存储过程 sql，支持设置参数类型
        [SqlExecute("exec PROP_NAME @id", CommandType = CommandType.StoredProcedure)]
        DataTable GetPerson(int id));

        // 支持多数据库操作
        [SqlExecute("select * from person", DbContextLocator = typeof(MySqlDbContextLocator))]
        DataTable GetPerson());

        // 异步方式
        [SqlExecute("select * from person", DbContextLocator = typeof(MySqlDbContextLocator))]
        Task<DataTable> GetPersonAsync());
    }
}
```

:::important 关于参数

`Sql` 代理参数查找规则：

如果方法的参数是 `基元类型`（或 `string`、`值类型`），则自动将这些类型组合成 `Dictionary<string, object>` 作为 `Sql` 参数。命令参数可使用方法同名参数加 `@` 符号。

如果方法的参数是 `类类型`，那么自动遍历该类公开实例属性生成 `DbParameter[]` 数组，每一个属性名都将是命令参数，不区分大小写，如：

```cs
public class MyModel
{
    public int Id {get;set;}
    public string Name {get; set;}
}
```

那么 `sql` 语句可以直接使用属性名作为参数：

```sql
select * from person where id > @id and name = @name;
```

:::

### 9.17.4.2 返回 `T 或 List<T>`

```cs {8,12,16,20}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        // 执行sql并传入参数，基元类型
        [SqlExecute("select * from person where id >@id and name like %@name%")]
        List<Person> GetPerson(int id, string name);

        // 执行sql并传入参数，对象类型
        [SqlExecute("select * from person where id >@id and name like %@name%")]
        List<Person> GetPerson(MyParam paras));

        // 执行存储过程 sql，支持设置参数类型
        [SqlExecute("exec PROP_NAME @id", CommandType = CommandType.StoredProcedure)]
        Person GetPerson(int id));

        // 支持多数据库操作
        [SqlExecute("select * from person", DbContextLocator = typeof(MySqlDbContextLocator))]
        List<Person> GetPerson());

        // 异步方式
        [SqlExecute("select * from person", DbContextLocator = typeof(MySqlDbContextLocator))]
        Task<List<Person>> GetPersonAsync());
    }
}
```

### 9.17.4.3 返回 `DataSet`

```cs {8-10,14-16,20-22,26-28,32-35}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        // 执行sql并传入参数，基元类型
        [SqlExecute(@"
            select * from person where id >@id and name like %@name%;
            select top 10 * from student where Id >@id;")]
        DataSet GetData(int id, string name);

        // 执行sql并传入参数，对象类型
        [SqlExecute(@"
            select * from person where id >@id and name like %@name%;
            select top 10 * from student where Id >@id;")]
        DataSet GetData(MyParam paras));

        // 执行存储过程 sql，支持设置参数类型
        [SqlExecute(@"
            exec PROP_NAME @id;
            select * from person;", CommandType = CommandType.StoredProcedure)]
        DataSet GetData(int id));

        // 支持多数据库操作
        [SqlExecute(@"
            select * from person;
            select * from student;", DbContextLocator = typeof(MySqlDbContextLocator))]
        DataSet GetData());

        // 异步方式
        [SqlExecute(@"
            select * from person;
            select * from student;
            select 1;", DbContextLocator = typeof(MySqlDbContextLocator))]
        Task<DataSet> GetDataAsync());
    }
}
```

### 9.17.4.4 返回 `Tuple<T1,...T8>`

```cs {8-10,14-16,20-22,26-28,32-35}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        // 执行sql并传入参数，基元类型
        [SqlExecute(@"
            select * from person where id >@id and name like %@name%;
            select top 10 * from student where Id >@id;")]
        (List<Person>,List<Student>) GetData(int id, string name);

        // 执行sql并传入参数，对象类型
        [SqlExecute(@"
            select * from person where id >@id and name like %@name%;
            select top 10 * from student where Id >@id;")]
        (List<Person>,List<Student>) GetData(MyParam paras));

        // 执行存储过程 sql，支持设置参数类型
        [SqlExecute(@"
            exec PROP_NAME @id;
            select * from person;", CommandType = CommandType.StoredProcedure)]
        (List<Person>,List<Student>) GetData(int id));

        // 支持多数据库操作
        [SqlExecute(@"
            select * from person;
            select * from student;", DbContextLocator = typeof(MySqlDbContextLocator))]
        (List<Person>,List<Student>) GetData());

        // 异步方式
        [SqlExecute(@"
            select * from person;
            select * from student;
            select 1;", DbContextLocator = typeof(MySqlDbContextLocator))]
        Task<(List<Person>,List<Student>,int)> GetDataAsync());
    }
}
```

### 9.17.4.5 返回 `单行单列`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlExecute("select Name from person where id = @id")]
        string GetValue(int id);

        [SqlExecute("select age from person where id = @id")]
        int GetValue(int id);

        [SqlExecute("select Name from person where id = @id")]
        Task<string> GetValueAsync(int id);
    }
}
```

### 9.17.4.6 无返回值

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlExecute("insert into person(Name,Age) values(@name,@age)")]
        void Insert(MyParam dto);

        [SqlExecute("delete from person where id = @id")]
        void Delete(int id);

        [SqlExecute("update person set name=@name where id=@id")]
        void Update(int id, string name);
    }
}
```

## 9.17.5 `存储过程` 操作

### 9.17.5.1 返回 `DataTable`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        DataTable GetPersons(MyParam dto);

        [SqlProcedure("PROC_Name")]
        DataTable GetPersons(int id);

        [SqlProcedure("PROC_Name")]
        DataTable GetPersons(int id, string name);
    }
}
```

### 9.17.5.2 返回 `T 或 List<T>`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        List<Person> GetPersons(MyParam dto);

        [SqlProcedure("PROC_Name")]
        List<Person> GetPersons(int id);

        [SqlProcedure("PROC_Name")]
        Person GetPersons(int id, string name);
    }
}
```

### 9.17.5.3 返回 `DataSet`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        DataSet GetData(MyParam dto);

        [SqlProcedure("PROC_Name")]
        DataSet GetData(int id);

        [SqlProcedure("PROC_Name")]
        DataSet GetData(int id, string name);
    }
}
```

### 9.17.5.4 返回 `Tuple(T1,...T8)`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        (List<Person>, List<Student>) GetData(MyParam dto);

        [SqlProcedure("PROC_Name")]
        (List<Person>, List<Student>) GetData(int id);

        [SqlProcedure("PROC_Name")]
        (List<Person>, List<Student>, Person, int) GetData(int id, string name);
    }
}
```

### 9.17.5.4 返回 `单行单列`

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        object GetValue(MyParam dto);

        [SqlProcedure("PROC_Name")]
        string GetValue(int id);

        [SqlProcedure("PROC_Name")]
        int GetValue(int id, string name);
    }
}
```

### 9.17.5.6 无返回值

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        void GetValue(MyParam dto);

        [SqlProcedure("PROC_Name")]
        void GetValue(int id);

        [SqlProcedure("PROC_Name")]
        void GetValue(int id, string name);
    }
}
```

### 9.17.5.7 带 `OUTPUT/RETURN` 返回

```cs {7,10,13}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlProcedure("PROC_Name")]
        ProcedureOutputResult GetOutput(ProcOutputModel pams);

        [SqlProcedure("PROC_Name")]
        ProcedureOutputResult GetOutput(ProcOutputModel pams);

        [SqlProcedure("PROC_Name")]
        ProcedureOutputResult<(List<Person>, List<Student>)> GetOutput(ProcOutputModel pams);
    }
}
```

## 9.17.5 `函数` 操作

```cs {7,10}
using Fur.DatabaseAccessor;

namespace Fur.Application
{
    public interface ISql : ISqlDispatchProxy
    {
        [SqlFunction("FN_Name")]    // 标量函数
        string GetValue(MyParam dto);

        [SqlProcedure("FN_Name")]   // 表值函数
        List<Person> GetPersons(int id);
    }
}
```

:::important 补充说明

`Sql` 代理会自动判断返回值然后自动执行特定函数类型。

:::

## 9.17.6 为什么用它？

通过上面的例子大家就可以了解，这种方式操作 `sql` 非常简单，而且极易维护。大家不用去关系返回值，关心用哪个方法，所有东西会自动给你处理好。

所以，如果需要用 `Sql` 操作，推荐使用 `Sql` 高级代理。

## 9.17.7 反馈与建议

:::note 与我们交流

给 Fur 提 [Issue](https://gitee.com/monksoul/Fur/issues/new?issue)。

:::
